/**
 * 
 */
package com.netscenery.yang.model;

import java.util.ArrayList;

import org.jdom2.Attribute;
import org.jdom2.Element;

import com.netscenery.util.ErrTag;
import com.netscenery.util.QName;
import com.netscenery.util.XMLTag;
import com.netscenery.yang.YangStatement;
import com.netscenery.yang.designer.MapInfo;
import com.netscenery.yang.parser.YangParserException;

/**
 * @author llly
 *
 */
public class Type extends ModelElement {

	protected boolean isDerivedType ;
	protected Typedef derived;
	protected BuiltinType base;


	public Type(){
		
	}
	public Type(Env env,BuiltinType base){
		this.setEnv(env);
		this.setBase(base);
		this.setName(base.getFieldName());
	}
	public Type(Env env,BuiltinType base,String name){
		this.setEnv(env);
		this.setBase(base);
		this.setName(name);
	}
	

	/**
	 * ����ǲ��ǺϷ���������Ϸ��������������������ͺ�
	 * ��ģ���Զ�����������Լ�����ģ���Զ�����������
	 * @param name
	 * @return
	 */
	public boolean isValidTypeName(String name){
		if (name.equals(base.getFieldName())){
			return true;
		}
		
		Typedef def = null;
		QName qname = new QName(name);
		def = Typedef.getDefByQName(this.getEnv(),qname);
		if (null == def){
			return false;
		}
		if (def.getType().getBase() != this.base){
			return false;
		}
	
		return true;
	}
		
	public boolean setName(String name){
		Typedef def = null;
		if (name.equals(base.getFieldName())){
			this.name = name;
			this.derived = null;
			this.isDerivedType = false;
			return true;
		}
		
		
		QName qname = new QName(name);
		def = Typedef.getDefByQName(this.getEnv(),qname);
		if (null == def){
			return false;
		}
		if (def.getType().getBase() != this.base){
			return false;
		}
		this.name = name;
		this.derived = def;				
		this.isDerivedType = true;
		return true;
	}

	
	/**
	 * @return the isDerivedType
	 */
	public boolean isDerivedType() {
		return isDerivedType;
	}

	
	
	/**
	 * @return the derived
	 */
	public Typedef getDerived() {
		return derived;
	}

	/**
	 * @param derived the derived to set
	 */
	public void setDerived(Typedef derived) {
		this.derived = derived;
	}

	public boolean checkValue(String value){
		if (getBase() == BuiltinType.BOOLEAN){
			if (value.equals("true")
					|| value.equals("false")){
				return true;
			}
			else {
				return false;
			}
		}
		return true;
	}

	public Type getBaseType(){
		if (!this.isDerivedType){
			return this;
		}
		
		Type type = this;
		while(type.isDerivedType){
			type = type.getDerived().getType();
		}
		return type;
	}

	/**
	 * @return the base
	 */
	public BuiltinType getBase() {
		return base;
	}

	/**
	 * @param base the base to set
	 */
	public void setBase(BuiltinType base) {
		this.base = base;
	}
	
	
	public static ArrayList<String> getAvailableDerivedTypes(Env env,String builtinType){
		if (null == builtinType){
			return null;
		}
		ArrayList<String> types = null;
		Typedef def = null;
		//���ұ��صĶ���
		Module curModule = env.getCurModule();
		if (null == curModule){
			return null;
		}
		int typedefsSize = curModule.getTypedefsNumber();
		for (int i = 0;i <typedefsSize; i++){
			def = curModule.getTypedef(i);
			if (null == def){
				continue;
			}
			
			if (def.getType().getBase().getFieldName().equals(builtinType)){
				if (null == types){
					types = new ArrayList<String>(1);
				}
				types.add(def.getName());
			}
		}
		Module m = env.getCurModule();
		//���ұ��ذ��ģ���еĶ���
		Env mEnv = m.getEnv();
		ArrayList<SubModule> sms = mEnv.getSubModules();
		if (null != sms){
			int SubModulesSize = sms.size();
			for (int i = 0; i < SubModulesSize ; i++){
				SubModule sm = sms.get(i);
				if (null == sm){
					continue;
				}
				typedefsSize = sm.getTypedefsNumber();
				for (int j= 0; j < typedefsSize;j++){
					def = sm.getTypedef(j);
					if (null == def){
						continue;
					}
					
					if (def.getType().getBase().getFieldName().equals(builtinType)){
						if (null == types){
							types = new ArrayList<String>(1);
						}
						types.add(def.getName());
					}
				}
				
			}
		}

		
		//�����ҵ���ģ��Ķ���
		int impsSize = m.getImportsNumber();
		
		for (int i = 0; i < impsSize; i ++){
			Import imp = m.getImport(i);
			if (null == imp){
				continue;
			}
			
			MainModule mm = imp.getImportedModule();
			if (null == mm){
				continue;
			}
			typedefsSize = mm.getTypedefsNumber();
			for (int j= 0; j < typedefsSize;j++){
				def = mm.getTypedef(j);
				if (null == def){
					continue;
				}
				
				if (def.getType().getBase().getFieldName().equals(builtinType)){
					if (null == types){
						types = new ArrayList<String>(1);
					}
					String typename = imp.getPrefix() + ":" + def.getName();
					
					types.add(typename);
				}
			}
			
		}

		
		return types;
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#validate()
	 */
	@Override
	public Validator validate() {
		Validator v = new Validator();
		if (isNullString(name)){
			v.setIsValid(false);
			v.addMsg(new Message(this, ErrTag.ERROR,ErrorCode.COMMON_INVALIDNAME));
		}
		if (this.isDerivedType){
			if (name.equals(base.getFieldName())){
				v.setIsValid(false);
				v.addMsg(new Message(this, ErrTag.ERROR,"invalid name," +
						"derived-type's name must be not the same to builtin-type's name."));
			}
		}
		else
		{
			if (!name.equals(base.getFieldName())){
				v.setIsValid(false);
				v.addMsg(new Message(this, ErrTag.ERROR,"invalid builtin-type's name." ));

			}
		}
		if(!isValidTypeName(name)){
			v.setIsValid(false);
			v.addMsg(new Message(this, ErrTag.ERROR,"invalid name," +
					"type's name must be builtin-type's name or derived-type's name."));
		}
		Validator s = super.validate();
		v.copy(s);
		
		return v;

	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toYang()
	 */
	@Override
	public YangStatement toYang() {

		YangStatement type = new YangStatement(
				YangStatement.YANG_STATEMENT_TYPE, this.name);
		return type;

	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#toXML()
	 */
	@Override
	public Element toXML() {
	
		Element commonType = new Element(XMLTag.XML_TAG_TYPE);
		Element type = null;
		if (true == this.isDerivedType) {
			type = new Element(XMLTag.XML_TAG_DERIVEDTYPE);
		} else {
			type = new Element(XMLTag.XML_TAG_BASETYPE);
		}

		type.setText(this.name);
		commonType.addContent(type);
		return commonType;
		
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#isFieldMandatory(java.lang.String)
	 */
	@Override
	public boolean isFieldMandatory(String field) {
		// TODO Auto-generated method stub
		return false;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readXML(org.jdom2.Element)
	 */
	@Override
	public int readXML(Element element) throws ModelException {
		return MODELELEMENT_OK;
	}

	/* (non-Javadoc)
	 * @see com.netscenery.yang.model.ModelElement#readYANG(com.netscenery.modeldesigner.util.YangStatement)
	 */
	@Override
	public int readYANG(YangStatement statement) throws YangParserException, ModelException {

		return MODELELEMENT_OK;
	}
	
	public static Type create(Env env,String name) {
		BuiltinType t = BuiltinType.getBuiltinType(name); 
		if (null == t){
			
			Typedef def = Typedef.getDefByQName(env,new QName(name));
			if (null == def){
				return null;
			}
			Type tt = def.getType();
			if (tt instanceof Bits){
				t = ((Bits)tt).getBase();
			}
			else if (tt instanceof Enumeration){
				t = ((Enumeration)tt).getBase();
			}
			else if (tt instanceof YangString){
				t = ((YangString)tt).getBase();
			}
			else if (tt instanceof YangInteger) {
				t = ((YangInteger)tt).getBase();
			}
			else if (tt instanceof Binary){
				t = ((Binary)tt).getBase();
			}
			else if (tt instanceof LeafRef){
				t = ((LeafRef)tt).getBase();
			}
			else if (tt instanceof Union){
				t = ((Union)tt).getBase();
			}
			else if (tt instanceof IdentityRef){
				t = ((IdentityRef)tt).getBase();
			}
			else
			{
				t = def.getType().getBase();
			}
			
		}
		if (null == t) {
			return null;
		}
		switch (t) {
		case BITS:{
			Bits bits = new Bits();
			bits.setEnv(env);
			bits.setBase(t);
			bits.setName(name);
			return bits;
		}
		case ENUMERATION: {
			Enumeration en = new Enumeration();
			en.setEnv(env);
			en.setBase(t);
			en.setName(name);
			return en;
		}
		case INT8:
		case INT16:
		case INT32:
		case INT64:
		case UINT8:
		case UINT16:
		case UINT32:
		case UINT64:{
			YangInteger integer = new YangInteger();
			integer.setEnv(env);
			integer.setBase(t);
			integer.setName(name);
			return integer;
		}
		case STRING:{
			YangString str = new YangString();
			str.setEnv(env);
			str.setBase(t);
			str.setName(name);			
			return str;
		}
		case BINARY:{
			Binary bin = new Binary();
			bin.setEnv(env);
			bin.setBase(t);
			bin.setName(name);
			return bin;
		}
		case LEAFREF:{
			LeafRef lr = new LeafRef();
			lr.setEnv(env);
			lr.setBase(t);
			lr.setName(name);
			return lr;
		}
		case UNION:{
			Union un = new Union();
			un.setEnv(env);
			un.setBase(t);
			un.setName(name);
			return un;
		}
		case IDENTITYREF:{
			IdentityRef ref= new IdentityRef();
			ref.setEnv(env);
			ref.setBase(t);
			ref.setName(name);
			return ref;
		}
		default:{
			Type type = new Type();
			type.setEnv(env);
			type.setBase(t);
			type.setName(name);
			return type;
		}
		}

	}
	public static String getTypeName(YangStatement statement){
		if (null == statement){
			return null;
		}
		if(!statement.getKeyword().equals(YangStatement.YANG_STATEMENT_TYPE)){
			return null;
		}
		
		return statement.getArgument();


	}
	
	public static String getTypeName(Element element){
		if (null == element){
			return null;
		}
		
		if (!element.getName().equals(XMLTag.XML_TAG_TYPE)){
			return null;
		}
		Element type = element.getChild(XMLTag.XML_TAG_BASETYPE);
		if (null == type){
			type = element.getChild(XMLTag.XML_TAG_DERIVEDTYPE);		
		}
		if (null == type){
			return null;
		}
		
		return type.getValue();
	}
	public  MapInfo getMapInfo() throws SecurityException, NoSuchMethodException {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public Element toYin() {

		Element type = new Element(
				YangStatement.YANG_STATEMENT_TYPE,env.getNs());
		Attribute name = new Attribute(XMLTag.XML_TAG_NAME, this.name);
		type.setAttribute(name);
		return type;
	}
	@Override
	public int readYin(Element element) throws ModelException {
		return MODELELEMENT_OK;
	}
	@Override
	public void update() {
		// TODO Auto-generated method stub
		
	}

}
